﻿using GTMC.Enum;
using GTMC.Model;
using Leeder.GTMC.Communi;
using Leeder.Logger;
using Leeder.Util;
using SPSAndong.Business;
using SPSAndong.Comm;
using SPSAndong.Enum;
using SPSAndong.Model;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using TitaniumAS.Opc.Client.Common;
using TitaniumAS.Opc.Client.Da;
namespace SPSAndong.Handle
{
	public class SPSAndongHandler
	{

		/// <summary>
		/// 日志
		/// </summary>
		LogOperate LogObj => Log.GetLogger(this.GetType());
		/// <summary>
		/// 按钮呼叫超时线程
		/// </summary>
		CancellationTokenSource cancel_buttonStateTimeOut;
        /// <summary>
        /// 按钮呼叫线程
        /// </summary>
        CancellationTokenSource cancel_buttonState;
        /// <summary>
        /// LIP线程
        /// </summary>
        CancellationTokenSource cancel_lip;
        /// <summary>
        /// 按钮状态改变事件
        /// </summary>
        public event Action OnButtonStateChanged;
		/// <summary>
		/// 设备状态改变事件
		/// </summary>
		public event Action OnDeviceStateChanged;

		/// <summary>
		/// 加班时间改变事件
		/// </summary>
		public event Action OnOuttimeChanged;
		/// <summary>
		/// 主要设备监控线程
		/// </summary>
		public CancellationTokenSource cancel_mainequip;
		/// <summary>
		/// 安东屏设备监控线程
		/// </summary>
		public CancellationTokenSource cancel_andonquip;
		/// <summary>
		/// 定义ups服务变量
		/// </summary>
		private UpsHandle upsServer;
		/// <summary>
		/// 程序编号
		/// </summary>
		public ProgramCode ProgramCode { get; set; } = ProgramCode.Program01;

		TriaConnectBLL triaConnectBLL = new TriaConnectBLL();
		TriaGroupBLL triaGroupBLL = new TriaGroupBLL();
		TriaItemBLL triaItemBLL = new TriaItemBLL();
		LedShowBLL ledShowBLL = new LedShowBLL();
		ProjectSetBLL projectSetBLL = new ProjectSetBLL();
		SystemSetupBLL systemSetupBLL = new SystemSetupBLL();
		AutoResetEvent atresetEvent = new AutoResetEvent(false);
        AutoResetEvent atresetEventLip = new AutoResetEvent(false);
        /// <summary>
        /// UPS状态变化事件
        /// </summary>
        public event Action<UpsModel> UpsChangeEvent;

		/// <summary>
		/// 单例模式
		/// </summary>
		private static SPSAndongHandler instance = new SPSAndongHandler();
		public static SPSAndongHandler getInstance()
		{
			if (instance == null)
				instance = new SPSAndongHandler();
			return instance;
		}
		/// <summary>
		/// 构造函数
		/// </summary>
		private SPSAndongHandler()
		{

		}
		public void Init()
		{
			InitData();
            InitToyopuc("BUTTON");
            InitToyopuc("");
			InitTask();
		}
		private void InitData()
		{
			//初始化服务
			var sysSet = systemSetupBLL.GetModel();
			GolbalVariable.ProjectBag = new ConcurrentBag<ProjectSetModel>(new ProjectSetBLL().GetModelAll());
			GolbalVariable.ProjectBag.ForEach(x => x.State = 0); //初始化状态为0
			GolbalVariable.AppSet = new AppSetModel();
			GolbalVariable.AppSet.AndongShowRefresh = 1;
			GolbalVariable.AppSet.ExceptionCall = sysSet.ExceptionCall;
			GolbalVariable.AppSet.ButtonWorkDispatchTimeSpan = 1000;
			GolbalVariable.AppSet.VoiceListenPort = 23;
			GolbalVariable.OpcHelper = new OpcHelper();
			GolbalVariable.VoiceAreaMusicList = new MusicSetBLL().GetMusicBroadcastSet();
			GolbalVariable.SystemDeviceBag = new ConcurrentBag<SystemDeviceModel>(new SystemDeviceBLL().GetModelAll());
			GolbalVariable.VoiceSever = new VoiceHelper();
			GolbalVariable.VoiceSever.InitSocket();
            GolbalVariable.PlcTriaList = new List<TriaModel>();
        }
        /// <summary>
        /// 初始化丰田工机
        /// </summary>
        public void InitToyopuc(string signalMark)
        {
            GolbalVariable.TriaConnectList = triaConnectBLL.GetModelList();
            GolbalVariable.TriaGroupList = triaGroupBLL.GetModelList();
            GolbalVariable.TriaItemList = triaItemBLL.GetModelList();

            var plcIds = GolbalVariable.TriaGroupList.Where(x => string.IsNullOrEmpty(signalMark) ? !x.SignalMark.Equals("BUTTON") : x.SignalMark.Equals(signalMark)).Select(x => x.TriaConnectID).Distinct().ToList();
            var triaConnectList = GolbalVariable.TriaConnectList.Where(x => plcIds.Contains(x.ID)).ToList();

            foreach (var conn in triaConnectList)
            {
                TriaModel triaModel = new TriaModel() { ID = conn.ID };
                if (GolbalVariable.PlcTriaList.Any(x => x.ID == conn.ID))
                    GolbalVariable.PlcTriaList.RemoveAll(x => x.ID == conn.ID);

                var tria = GolbalVariable.PlcTriaList.FirstOrDefault(x => x.Ip == conn.Ip && x.Port == conn.Port);
                //同一个IP和端口，限制一个handler
                if (tria != null)
                    triaModel.ToyopucHandler = tria.ToyopucHandler;
                else
                    triaModel.ToyopucHandler = Leeder.GTMC.Communi.ToyopucInstance.GetInstance(conn.Ip, conn.Port);
                GolbalVariable.PlcTriaList.Add(triaModel);
                triaModel.Ip = conn.Ip;
				triaModel.Port = conn.Port;
				triaModel.AccessMode = conn.AccessMode;
				triaModel.ProgramCode = (ProgramCode)conn.ProgramCode;

				triaModel.TriaAddress = new IndirectAddress();
				triaModel.TriaAddress.Identifier = conn.Identifier;
				triaModel.TriaAddress.Address = (uint)conn.Address;
				triaModel.AddressLength = (short)conn.AddressLength;
				triaModel.TriaGroups = new List<TriaGroup>();
				for (uint i = triaModel.TriaAddress.Address; i < (triaModel.TriaAddress.Address + triaModel.AddressLength); i++)
				{
					var groupId = triaModel.TriaAddress.Identifier + i.ToString("X");
					var group = GolbalVariable.TriaGroupList.Where(s => s.GroupAddress == groupId && s.TriaConnectID == conn.ID).FirstOrDefault();
					if (group == null)
						continue;
					TriaGroup triaGroup = new TriaGroup
					{
						GroupId = groupId,
						GroupDataType = group.GroupType == 0 ? TriaDataType.Booleans : TriaDataType.Integer,
						Items = new List<TriaItem>()
					};
					triaModel.TriaGroups.Add(triaGroup);
					switch (triaGroup.GroupDataType)
					{
						case TriaDataType.Integer:
							triaGroup.Items.Add(new TriaItem
							{
								ItemId = triaGroup.GroupId + "_0"
							});
							break;
						case TriaDataType.Booleans:
							for (int j = 0; j < 16; j++)
							{
								triaGroup.Items.Add(new TriaItem
								{
									ItemId = triaGroup.GroupId + "_" + j.ToString("X")
								});
							}
							break;
					}
				}
			}
		}



	private void InitTask()
	{
			//呼叫超时监控线程(判断是否有呼叫超时)
			cancel_buttonStateTimeOut = new CancellationTokenSource();
			Task.Factory.StartNew(() =>
			{
				ButtonStateTimeOut();
			}, cancel_buttonStateTimeOut.Token);

			//丰田工机数据监控线程(读取数据，判断是否有按钮按下)
			cancel_buttonState = new CancellationTokenSource();
			Task.Factory.StartNew(() =>
			{
				ButtonState();
			}, cancel_buttonState.Token);
			TriaMonitor();

            //丰田工机LIP数据监控线程(读取加班时间和计划实际台数)
            cancel_lip = new CancellationTokenSource();
            Task.Factory.StartNew(() =>
            {
                LipState();
            }, cancel_lip.Token);
            TriaLIPMonitor();

            //主要设备监控线程
            if (GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "IP").Any())
			{
				cancel_mainequip = new CancellationTokenSource();
				LogObj.Info("开启主要设备监控线程启动");
				Task.Factory.StartNew(() =>
				{
					BindMainPower();
				}, cancel_mainequip.Token);
			}

			//创建ups监控对象的实例
			upsServer = new UpsHandle();
			//接收数据事件
			upsServer.Received += UpsServer_Received;
			//初始化com口
			LogObj.Info("ups监控线程开启");
			upsServer.InitCom();
		}

        /// <summary>
        /// 丰田工机LIP通讯监控
        /// </summary>
        public void TriaLIPMonitor()
        {
            Thread thread = new Thread(() =>
            {
                while (true)
                {
                    bool re = atresetEventLip.WaitOne(300000);//等300秒，300秒没有信号则取消旧线程重新创建读取线程
                    if (re)
                    {
                        Thread.Sleep(3000);
                    }
                    else
                    {
                        LogObj.Error("丰田工机Lip通讯异常，无法读取数据！");
                        //丰田工机数据监控线程(读取数据，判断是否有按钮按下)
                        cancel_lip.Cancel();
                        Thread.Sleep(300);
                        cancel_lip = new CancellationTokenSource();
                        Task.Factory.StartNew(() =>
                        {
                            InitToyopuc("");
                            LipState();
                        }, cancel_lip.Token);
                    }
                }
            }
            );
            thread.IsBackground = true;
            thread.Start();
        }

        /// <summary>
        /// 工程网(加班时间)线程
        /// </summary>
        private async void LipState()
        {
            LogObj.Info("工厂网(加班时间)线程开始");
            while (!this.cancel_lip.Token.IsCancellationRequested)
            {
                //找出其他PLC(LIP)
                var plcIds = GolbalVariable.TriaGroupList.Where(x => !x.SignalMark.Equals("BUTTON")).Select(x => x.TriaConnectID).Distinct().ToList();
                var plcTrialList = GolbalVariable.PlcTriaList.Where(x => plcIds.Contains(x.ID)).ToList();
                //遍历所有连接
                foreach (var plcTria in plcTrialList)
                {
                    try
                    {
                        await Task.Delay(20);
                        //如果连接
                        if (plcTria.AccessMode == "writeOnly")
                            continue;
                        var toyopucHandler = plcTria.ToyopucHandler;
                        byte[] result = null;
                        try
                        {
                            //await Task.Delay(10000);//测试代码

                            //查询当前按钮的状态
                            result = toyopucHandler.ReadExtString(plcTria.ProgramCode, plcTria.TriaAddress, plcTria.AddressLength); //PublicHelper.ReadPlcDataTest(plcTria);//测试


                            if (result.Length > 0)
                                atresetEventLip.Set();
                        }
                        catch (Exception ex)
                        {
                            LogObj.Error("LIP PLC读取失败：" + ex.Message);
                            continue;
                        }

                        //将读到的字节转换成二进制字符串
                        var resultStr = ByteToStr(result);

                        if (string.IsNullOrEmpty(resultStr))
                            return;

                        //比较当前读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理
                        if (plcTria.DataStr != resultStr)
                        {
                            plcTria.DataByte = result;
                            plcTria.DataStr = resultStr;
                            int packetIndex = 0;
                            //循环判断每一组数据
                            for (uint i = plcTria.TriaAddress.Address; i < (plcTria.TriaAddress.Address + plcTria.AddressLength); i++)
                            {
                                try
                                {
                                    //根据组ID获取该组
                                    var triaGroup = plcTria.TriaGroups.Where(s => s.GroupId == plcTria.TriaAddress.Identifier + i.ToString("X")).FirstOrDefault();

                                    if (triaGroup == null)
                                        continue;
                                    //截取该组数据(转换后的二进制字符串)
                                    var groupStates = resultStr.FindIndexString(ref packetIndex, 16);
                                    //比较该组当前读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理
                                    if (triaGroup.GroupStrs != groupStates)
                                    {
                                        triaGroup.Value = BitConverter.ToInt16(result, (int)((i - plcTria.TriaAddress.Address) * 2));
                                        LogObj.Debug($"{triaGroup.GroupId}:{triaGroup.Value.ToString()}");
                                        triaGroup.GroupStrs = groupStates;
                                        var group = GolbalVariable.TriaGroupList.Where(s => s.GroupAddress == plcTria.TriaAddress.Identifier + i.ToString("X")).FirstOrDefault();
                                        var items = GolbalVariable.TriaItemList.Where(s => s.GroupID == group.ID).ToList();
                                        //根据组名分别调用不同的逻辑方法
                                        switch (triaGroup.GroupDataType)
                                        {
                                            case TriaDataType.Integer:
                                                //比较当前项读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理          
                                                if (triaGroup.Items[0].Value == null || (triaGroup.Items[0].Value != triaGroup.Value))
                                                {
                                                    //将读到的二进制数转成十进制数并更新到数据模型中
                                                    triaGroup.Items[0].Value = triaGroup.Value;
                                                    switch (group.SignalMark)
                                                    {
                                                        case "OUTTIME":
                                                            LogObj.Info($"加班时间变化");
                                                            LogObj.Info($"加班时间-->{triaGroup.Items[0].ItemId}:{triaGroup.Items[0].Value.ToString()}");
                                                            GolbalVariable.Overtime = triaGroup.Items[0].Value.ToInt();
                                                            ledShowBLL.UpdateOverTime(GolbalVariable.Overtime);
                                                            OnOuttimeChanged?.BeginInvoke(null, null);
                                                            break;
                                                    }
                                                }
                                                break;
                                            case TriaDataType.Booleans:
                                                for (int j = 0; j < 16; j++)
                                                {
                                                    //比较当前项读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理
                                                    if (triaGroup.Items[j].Value == null || ((int)triaGroup.Items[j].Value != int.Parse(groupStates[j].ToString())))
                                                    {
                                                        var item = items.Where(s => s.ItemAddress == triaGroup.Items[j].ItemId).FirstOrDefault();
                                                        //将读到的二进制数转成单个0或1并更新到数据模型中
                                                        triaGroup.Items[j].Value = int.Parse(groupStates[j].ToString());
                                                        switch (group.SignalMark)
                                                        {
                                                            case "SBJK":
                                                                DeviceMonitor(items[j].SignalMark, (int)triaGroup.Items[j].Value);
                                                                break;
                                                        }
                                                    }
                                                }
                                                break;
                                        }
                                    }
                                }
                                catch (Exception ex)
                                {
                                    LogObj.Error(ex.Message);
                                    continue;
                                }
                            }
                        }

                        //休眠100豪秒
                        await Task.Delay(100);

                    }
                    catch (Exception exce)
                    {
                        LogObj.Error(exce.Message);
                    }
                }
                //休眠
                await Task.Delay(100);
            }
            LogObj.Info("工厂网(加班时间)线程开始");
        }

        /// <summary>
        /// 呼叫按钮状态改变
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonAdderssGroup_ValuesChanged(object sender, OpcDaItemValuesChangedEventArgs e)
		{
			try
			{
				foreach (OpcDaItemValue value in e.Values)
				{
					if (value == null || value.Value == null)
						continue;
					var info = GolbalVariable.OpcTagBag.Where(x => x.TagName == value.Item.ItemId).FirstOrDefault();
					if (info != null)
					{
						int result = 0;
						//判断OPC值类型并统一转换为INT类型（BOOL类型true=1，false=0）
						if (value.Value is bool)
							result = (Convert.ToBoolean(value.Value) ? 1 : 0);
						else
							int.TryParse(value.Value.ToString(), out result);
						//result==1时为过点信息,result==0时不作处理
						if (result == 0)
							continue;
						OnButtonStateChanged?.BeginInvoke(null, null);
						//业务处理
						MonitorButtonStateChange(info.SignalMark, result);
					}
				}
			}
			catch (Exception ex)
			{
				LogObj.Error(ex.Message);
			}
		}

		/// <summary>
		/// 系统设备故障监控
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void IoCommunicationAddressGroup_ValuesChanged(object sender, OpcDaItemValuesChangedEventArgs e)
		{
			try
			{
				foreach (OpcDaItemValue value in e.Values)
				{
					if (value == null || value.Value == null)
						continue;
					var info = GolbalVariable.OpcTagBag.Where(x => x.TagName == value.Item.ItemId).FirstOrDefault();
					if (info != null)
					{
						int result = 0;
						//判断OPC值类型并统一转换为INT类型（BOOL类型true=1，false=0）
						if (value.Value is bool)
							result = (Convert.ToBoolean(value.Value) ? 1 : 0);
						else
							int.TryParse(value.Value.ToString(), out result);
						OnDeviceStateChanged?.BeginInvoke(null, null);
						//业务处理
						DeviceStateChange(info.SignalMark, result);
					}
				}
			}
			catch (Exception ex)
			{
				LogObj.Error(ex.Message);
			}
		}

		/// <summary>
		/// 加班时间改变事件
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OuttimeAddressGroup_ValuesChanged(object sender, OpcDaItemValuesChangedEventArgs e)
		{
			try
			{
				foreach (OpcDaItemValue value in e.Values)
				{
					if (value == null || value.Value == null)
						continue;
					var info = GolbalVariable.OpcTagBag.Where(x => x.TagName == value.Item.ItemId).FirstOrDefault();
					if (info != null)
					{
						int result = 0;
						//判断OPC值类型并统一转换为INT类型（BOOL类型true=1，false=0）
						if (value.Value is bool)
							result = (Convert.ToBoolean(value.Value) ? 1 : 0);
						else
							int.TryParse(value.Value.ToString(), out result);
						GolbalVariable.Overtime = result;
						OnOuttimeChanged?.BeginInvoke(null, null);
						//业务处理
					}
				}
			}
			catch (Exception ex)
			{
				LogObj.Error(ex.Message);
			}
		}

		/// <summary>
		/// 设备监控
		/// </summary>
		/// <param name="signalMark">信号源</param>
		/// <param name="state">状态值</param>
		private void DeviceMonitor(string signalMark, int state)
		{
			var plans = GolbalVariable.SystemDeviceBag.Where(x => x.SignalMark == signalMark && x.DataSource == "PLC").ToList();
			if (plans != null)
				plans.ForEach(x =>
				{
					x.Status = state;
				});
		}


		private void MonitorButtonStateChange(string signalMark, int state)
		{
			//查找OPC触发对应的按钮
			var plans = GolbalVariable.ProjectBag.Where(x => x.SignalMark == signalMark).ToList();
			if (plans != null)
			{
				plans.ForEach(x =>
				{
					if (x.State != state)
					{
						if (x.State == 0)
						{
							//正常状态下按下按钮该部品状态变为呼叫状态
							x.ActualStartTime = DateTime.Now; //按钮状态为,1，开始呼叫，记录实际开始时间                                                            
							GolbalVariable.VoiceSever.ExecVoiceCommand(x.AreaType, true);//将讲呼岗位加入音乐播放队列
																						 //写入LED显示数据库表：呼叫异常显示：0：正常(完成)、1：异常呼叫、2：呼叫超时
							x.State = 1;

							//保存呼叫记录
							LogObj.Info(x.AreaName + "->" + x.WorkName + ":异常呼叫");
						}
						else
						{
							//呼叫或超时呼叫状态下按下按钮该部品状态变为正常状态
							x.ActualEndTime = DateTime.Now; //按钮状态为,0，结束呼叫，记录实际结束时间                                                             

							x.State = 0;
							if (GolbalVariable.ProjectBag.Count(s => s.State != 0 && s.AreaType == x.AreaType) == 0)
								GolbalVariable.VoiceSever.ExecVoiceCommand(x.AreaType, false, "", Convert.ToInt32((DateTime.Now - x.ActualStartTime.Value).TotalSeconds)); //将结束音乐播放，加入队列

							//保存呼叫记录
							LogObj.Info(x.AreaName + "->" + x.WorkName + ":呼叫完成");
						}
					}
				});
				projectSetBLL.UpdateList(plans);
			}
		}

		private void DeviceStateChange(string signalMark, int state)
		{
			//查找OPC触发对应的设备
			var plans = GolbalVariable.SystemDeviceBag.Where(x => x.SignalMark == signalMark).ToList();
			if (plans != null)
				plans.ForEach(x =>
				{
					x.Status = state;
				});
		}
		public void TriaMonitor()
		{
			Thread thread = new Thread(() =>
			{
				while (true)
				{
					bool re = atresetEvent.WaitOne(60000);//等60秒，60秒没有信号则取消旧线程重新创建读取线程
					if (re)
					{

					}
					else
					{
						LogObj.Error("丰田工机通讯异常，无法读取数据！");
						//丰田工机数据监控线程(读取数据，判断是否有按钮按下)
						cancel_buttonState.Cancel();
						Thread.Sleep(300);
						cancel_buttonState = new CancellationTokenSource();
						Task.Factory.StartNew(() =>
						{
							InitToyopuc("BUTTON");
							ButtonState();
						}, cancel_buttonState.Token);
					}
				}
			}
			);
			thread.IsBackground = true;
			thread.Start();
		}




		/// <summary>
		/// 按钮呼叫线程
		/// </summary>
		private async void ButtonState()
		{
			LogObj.Info("呼叫监控线程开始");
			while (!this.cancel_buttonState.Token.IsCancellationRequested)
			{
                //找出按钮PLC
                var plcIds = GolbalVariable.TriaGroupList.Where(x => x.SignalMark.Equals("BUTTON")).Select(x => x.TriaConnectID).Distinct().ToList();
                var plcTrialList = GolbalVariable.PlcTriaList.Where(x => plcIds.Contains(x.ID)).ToList();
                //遍历所有连接
                foreach (var plcTria in plcTrialList)
                {
					try
					{
						await Task.Delay(20);
						//如果连接
						if (plcTria.AccessMode == "writeOnly")
							continue;
						var toyopucHandler = plcTria.ToyopucHandler;
						byte[] result = null;
						try
						{
							//查询当前按钮的状态
							result = toyopucHandler.ReadExtString(plcTria.ProgramCode, plcTria.TriaAddress, plcTria.AddressLength);//PublicHelper.ReadPlcDataTest(plcTria);//测试
                            if (result.Length > 0)
								atresetEvent.Set();
						}
						catch (Exception ex)
						{
							LogObj.Error("PLC读取失败：" + ex.Message);
							continue;
						}
						//将读到的字节转换成二进制字符串
						string resultStr = ByteToStr(result);
						//比较当前读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理
						if (plcTria.DataStr != resultStr)
						{
							plcTria.DataByte = result;
							plcTria.DataStr = resultStr;
							int packetIndex = 0;
							//循环判断每一组数据
							for (uint i = plcTria.TriaAddress.Address; i < (plcTria.TriaAddress.Address + plcTria.AddressLength); i++)
							{
								try
								{
									//根据组ID获取该组
									var triaGroup = plcTria.TriaGroups.Where(s => s.GroupId == plcTria.TriaAddress.Identifier + i.ToString("X")).FirstOrDefault();
									if (triaGroup == null)
										continue;
									//截取该组数据(转换后的二进制字符串)
									var groupStates = resultStr.FindIndexString(ref packetIndex, 16);
									//比较该组当前读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理
									if (triaGroup.GroupStrs != groupStates)
									{
										triaGroup.Value = BitConverter.ToInt16(result, (int)((i - plcTria.TriaAddress.Address) * 2));
										triaGroup.GroupStrs = groupStates;
										var group = GolbalVariable.TriaGroupList.Where(s => s.GroupAddress == plcTria.TriaAddress.Identifier + i.ToString("X")).FirstOrDefault();
										var items = GolbalVariable.TriaItemList.Where(s => s.GroupID == group.ID).ToList();
										//根据组名分别调用不同的逻辑方法
										switch (triaGroup.GroupDataType)
										{
											case TriaDataType.Integer:

												break;
											case TriaDataType.Booleans:
												for (int j = 0; j < 16; j++)
												{
													//比较当前项读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理
													if (triaGroup.Items[j].Value == null || ((int)triaGroup.Items[j].Value != int.Parse(groupStates[j].ToString())))
													{
														var item = items.Where(s => s.ItemAddress == triaGroup.Items[j].ItemId).FirstOrDefault();
														//将读到的二进制数转成单个0或1并更新到数据模型中
														triaGroup.Items[j].Value = int.Parse(groupStates[j].ToString());
														switch (group.SignalMark)
														{
															case "BUTTON":
																MonitorButtonStateChange(items[j].SignalMark, (int)triaGroup.Items[j].Value);
																break;
															case "SBJK":
																DeviceMonitor(items[j].SignalMark, (int)triaGroup.Items[j].Value);
																break;
														}
													}
												}
												break;
										}
									}
								}
								catch (Exception ex)
								{
									LogObj.Error(ex.Message);
									break;
								}
							}
						}
					}
					catch (Exception exce)
					{
						LogObj.Error(exce.Message);
					}
				}
				//休眠
				await Task.Delay(100);
			}
			LogObj.Info("呼叫监控线程结束");
		}

		/// <summary>
		/// 异常呼叫状态超时线程
		/// </summary>
		private async void ButtonStateTimeOut()
		{
			while (!this.cancel_buttonStateTimeOut.Token.IsCancellationRequested)
			{
				//查询当前正在呼叫的按钮
				var datas = GolbalVariable.ProjectBag.Where(x => x.State == 1).ToList();
				if (datas.Count > 0)
				{
					foreach (var item in datas)
					{
						if (item.ActualStartTime != null)
						{
							if ((DateTime.Now - item.ActualStartTime.Value).TotalSeconds >= GolbalVariable.AppSet.ExceptionCall)
							{
								//当异常呼叫的时间超过配置时间，默认30秒
								item.UpdateTime = DateTime.Now;
								item.IsCallOutTime = true;
								item.State = 2;
								projectSetBLL.Update(item);
								LogObj.Info(item.AreaName + "->" + item.WorkName + ":呼叫超时");
							}
						}
					}
				}
				//休眠
				await Task.Delay(GolbalVariable.AppSet.ButtonWorkDispatchTimeSpan);
			}
		}




		private bool plcStatus = true;
		private bool lipStatus = true;
		/// <summary>
		/// 绑定硬件设备
		/// </summary>
		private async void BindMainPower()
		{
			var plc = GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "IP" && x.DeviceName == "PLC").FirstOrDefault();
			var lip = GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "IP" && x.DeviceName == "LIP").FirstOrDefault();
			string plcIp = plc.DeviceIpAddress;
			string lipIp = lip.DeviceIpAddress;
			while (true)
			{
				if (cancel_mainequip.Token.IsCancellationRequested)
				{
					cancel_mainequip.Dispose();
					break;
				}
				plcStatus = PublicHelper.PingIp(plcIp);
				lipStatus = PublicHelper.PingIp(lipIp);
				if (!plcStatus && GolbalVariable.PlcState != false)
				{
					LogObj.Error("PLC连接异常");
					GolbalVariable.PlcState = false;
					OnDeviceStateChanged?.BeginInvoke(null, null);
				}
				else
				{ GolbalVariable.PlcState = plcStatus; }
				if (!lipStatus && GolbalVariable.LipState != false)
				{
					LogObj.Error("LIP连接异常");
					GolbalVariable.LipState = false;
					OnDeviceStateChanged?.BeginInvoke(null, null);
				}
				else
				{ GolbalVariable.LipState = lipStatus; }

				await Task.Delay(5000);
			}
		}

		/// <summary>
		/// UPS接收数据事件
		/// </summary>
		/// <param name="data"></param>
		private void UpsServer_Received(UpsModel data)
		{
			try
			{
				LogObj.Debug(DateTime.Now + ": 输入电压:" + data.InputVoltage + "  输出电压:" + data.OutputVoltage + "  负载:" + data.Load + "  温度:" + data.Temperature + "  负载:" + data.Load);
				//把UPS当前状态更新到缓存中（用于界面状态的更新）
				GolbalVariable.UpsStatus = data;
				//数据处理
				UpsBusiness.CheckUps(data);
				UpsChangeEvent?.BeginInvoke(GolbalVariable.UpsStatus, null, null);
			}
			catch (Exception ex)
			{
				LogObj.Error(ex.ToString());
			}
		}

		/// <summary>
		/// 字节数组转二进制字符串
		/// </summary>
		/// <param name="bytes"></param>
		/// <returns></returns>
		private string ByteToStr(byte[] bytes)
		{
			string strResult = "";
			for (int i = 0; i < bytes.Length; i++)
			{
				string strTemp = Convert.ToString(bytes[i], 2);
				strTemp = strTemp.Insert(0, new string('0', 8 - strTemp.Length));
				//倒叙
				strTemp = ReverseD(strTemp);
				strResult += strTemp;
			}
			return strResult;
		}

		/// <summary>
		/// 二进制字符串转字节数组
		/// </summary>
		/// <param name="str"></param>
		/// <returns></returns>
		private byte[] StrToByte(string str)
		{
			byte[] bytes = new byte[str.Length / 8];
			for (int i = 0; i < bytes.Length; i++)
			{
				bytes[i] = Convert.ToByte(str.Substring(i * 8, 8), 2);
			}
			return bytes;
		}

		/// <summary>
		/// 字符串倒叙
		/// </summary>
		/// <param name="text"></param>
		/// <returns></returns>
		public string ReverseD(string text)
		{
			return new string(text.ToCharArray().Reverse().ToArray());
		}
	}
}
